package org.codehaus.groovy.grails.web.pages;

import groovy.lang.Writable;
import groovy.text.Template;

import java.io.IOException;
import java.io.Writer;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.StrutsResultSupport;
import org.codehaus.groovy.grails.web.servlet.DefaultGrailsApplicationAttributes;
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes;
import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest;
import org.springframework.core.io.Resource;
import org.springframework.web.context.request.RequestContextHolder;

import com.opensymphony.xwork2.ActionInvocation;

/**
 * Result type that renders grails server pages.
 */
public class GSPResult extends StrutsResultSupport implements StrutsStatics {

	private GrailsApplicationAttributes grailsAttributes;
	/**
	 * The size of the buffer used when formulating the response
	 */
	private static final int BUFFER_SIZE = 8192;

	public GSPResult() {
		super();
	}

	public GSPResult(String location, boolean parse, boolean encode) {
		super(location, parse, encode);
	}

	public GSPResult(String location) {
		super(location);
	}

	@Override
	protected void doExecute(String finalLocation, ActionInvocation invocation)
			throws Exception {
		HttpServletRequest request = (HttpServletRequest) invocation.getInvocationContext().get(
				HTTP_REQUEST);
		HttpServletResponse response = (HttpServletResponse) invocation.getInvocationContext().get(
				HTTP_RESPONSE);
		ServletContext context = (ServletContext) invocation
				.getInvocationContext().get(SERVLET_CONTEXT);
		doExecute(finalLocation, request, response, context);
	}

	public void doExecute(String finalLocation, HttpServletRequest request,
			HttpServletResponse response, ServletContext context)
			throws Exception {
		if (grailsAttributes == null) {
			// Get the servlet context
			context.log("GSP servlet initialized");
			this.grailsAttributes = new DefaultGrailsApplicationAttributes(
					context);
		}
		request.setAttribute(GrailsApplicationAttributes.GSP_TO_RENDER,
				finalLocation);
		doPage(request, response, context);
	}

	/**
	 * Execute page and produce output.
	 * 
	 * @param request
	 *            The HttpServletRequest insance
	 * @param response
	 *            The HttpServletResponse instance
	 * @throws ServletException
	 *             Thrown when an exception occurs executing the servlet
	 * @throws IOException
	 *             Thrown when an IOException occurs executing the servlet
	 */
	public void doPage(HttpServletRequest request,
			HttpServletResponse response, ServletContext context)
			throws ServletException, IOException {
		request.setAttribute(GrailsApplicationAttributes.REQUEST_SCOPE_ID,
				grailsAttributes);

		GroovyPagesTemplateEngine engine = grailsAttributes
				.getPagesTemplateEngine();
		String pageName = (String) request
				.getAttribute(GrailsApplicationAttributes.GSP_TO_RENDER);
		if (StringUtils.isEmpty(pageName)) {
			pageName = engine.getCurrentRequestUri(request);
		}

		Resource page = engine.getResourceForUri(pageName);
		if (page == null) {
			context.log("GroovyPagesServlet:  \"" + pageName + "\" not found");
			response.sendError(404, "\"" + pageName + "\" not found.");
			return;
		}

		renderPageWithEngine(engine, request, response, context, page);
	}

	/**
	 * Attempts to render the page with the given arguments
	 * 
	 * @param engine
	 *            The GroovyPagesTemplateEngine to use
	 * @param request
	 *            The HttpServletRequest
	 * @param response
	 *            The HttpServletResponse
	 * @param pageResource
	 *            The URL of the page
	 * 
	 * @throws IOException
	 *             Thrown when an I/O exception occurs rendering the page
	 * @throws ServletException
	 *             Thrown when an exception occurs in the servlet environment
	 */
	protected void renderPageWithEngine(GroovyPagesTemplateEngine engine,
			HttpServletRequest request, HttpServletResponse response,
			ServletContext context, Resource pageResource) throws IOException,
			ServletException {
		Writer out = createResponseWriter(response);
		try {
			Template t = engine.createTemplate(pageResource);
			if (t == null) {
				context.log("GroovyPagesServlet:  \""
						+ pageResource.getDescription() + "\" not found");
				response.sendError(404, "\"" + pageResource.getDescription()
						+ "\" not found.");
				return;
			}
			Writable w = t.make();

			w.writeTo(out);
		} finally {
			if (out != null)
				out.close();
		}
	}

	/**
	 * Creates a response writer for the given response object
	 * 
	 * @param response
	 *            The HttpServletResponse
	 * @return The created java.io.Writer
	 */
	protected Writer createResponseWriter(HttpServletResponse response) {
		Writer out = GSPResponseWriter.getInstance(response, BUFFER_SIZE);
		GrailsWebRequest webRequest = (GrailsWebRequest) RequestContextHolder
				.currentRequestAttributes();
		webRequest.setOut(out);
		return out;
	}
}
